﻿#include <iostream>
#include <string_view>
#include <cpprest/http_client.h>
#include <cpprest/http_msg.h>
#include <cpprest/json.h>
using namespace std;
using namespace std::literals;
using namespace utility;
using namespace web::http;
using namespace web::http::client;
using namespace web::json;

using string_view_t = basic_string_view<string_t::value_type, string_t::traits_type>;

// http字面量

// form
constexpr string_view_t kContentTypeForm = U("application/x-www-form-urlencoded"sv);
// json
constexpr string_view_t kContentTypeJson = U("application/json"sv);

// 路由器配置

// 登录请求
constexpr string_view_t kRequestUrl = U("/cgi-bin/luci"sv);
// 设置页面
constexpr string_view_t kSettingsUrl = U("/admin/gwsettings"sv);
// 登录响应设置cookie
constexpr string_view_t kSetCookie = U("Set-Cookie"sv);
// cookie首页路径key
constexpr string_view_t kPathKey = U("path="sv);
// cookie系统验证码key
constexpr string_view_t kSysauthKey = U("sysauth="sv);
// cookie系统验证码长度
constexpr string_t::size_type kSysauthLength = 32;
// 开启2.4G
constexpr string_view_t kTurnOn24G = U("getinfo=setWifiradioenable24G&data={\"wifiradioenable24G\":{\"radioenable\":\"1\"}}"sv);
// 关闭2.4G
constexpr string_view_t kTurnOff24G = U("getinfo=setWifiradioenable24G&data={\"wifiradioenable24G\":{\"radioenable\":\"0\"}}"sv);
// 开启5G
constexpr string_view_t kTurnOn5G = U("getinfo=setWifiradioenable5G&data={\"wifiradioenable5G\":{\"radioenable\":\"1\"}}"sv);
// 关闭5G
constexpr string_view_t kTurnOff5G = U("getinfo=setWifiradioenable5G&data={\"wifiradioenable5G\":{\"radioenable\":\"0\"}}"sv);
// 2.4G响应json对象key
constexpr string_view_t kResponseObject24G = U("wifiradioenable24G"sv);
// 5G响应json对象key
constexpr string_view_t kResponseObject5G = U("wifiradioenable5G"sv);
// 响应json对象返回值key
constexpr string_view_t kReturnCode = U("returncode"sv);
// 响应json对象返回成功值
constexpr string_view_t kSuccessCode = U("0"sv);

// 自定义部分

// 你家路由器ip地址
constexpr string_view_t kIpAddress = U("http://192.168.1.1"sv);
// 账号密码
constexpr string_view_t kLoginBody = U("username=admin&password=admin"sv);

/**
 * @brief 设置wifi状态
 * @param set5G 是否设置5G状态
 * @param bOn 开关
 * @return 任务
*/
pplx::task<void> setting(const bool set5G, const bool bOn)
{
    // POST请求
    http_request request(methods::POST);
    // 登录需要使用form登录
    request.headers().set_content_type(kContentTypeForm.data());
    // 设置form内容
    request.set_body(kLoginBody.data());
    // POST到登录页面
    request.set_request_uri(kRequestUrl.data());

    http_client client(kIpAddress.data());

    // 发送请求
    return client.request(request).then([](http_response response) {
        // 响应头
        const auto &headers = response.headers();

        // cookie
        string_t cookie;
        if (!headers.match(kSetCookie.data(), cookie))
            throw http_exception(U("登录失败"));

        const auto posPathKey = cookie.find(kPathKey);

        // 没有返回路径视为登录失败
        if (posPathKey == string_t::npos)
            throw http_exception(U("登录失败"));

        // 系统验证码
        const auto &sysauth = cookie.substr(kSysauthKey.size(), kSysauthLength);
        // 路径
        const auto &path = cookie.substr(posPathKey + kPathKey.size());

        return make_pair(sysauth, path);
    }).then([set5G, bOn](const pair<string_t, string_t> &info) {
        // [系统验证码, 路径]
        auto [sysauth, requestUrl] = info;

        requestUrl += kSettingsUrl;

        // POST请求
        http_request request(methods::POST);
        // 设置状态需要使用form
        request.headers().set_content_type(kContentTypeForm.data());
        // 还需要系统验证码
        request.headers().add(U("Cookie"), kSysauthKey.data() + sysauth);
        // 按需设置请求内容
        if (set5G)
            if (bOn)
                request.set_body(kTurnOn5G.data());
            else
                request.set_body(kTurnOff5G.data());
        else
            if (bOn)
                request.set_body(kTurnOn24G.data());
            else
                request.set_body(kTurnOff24G.data());
        // POST到设置页面
        request.set_request_uri(requestUrl);

        http_client client(kIpAddress.data());

        // 发送请求
        return client.request(request);
    }).then([](http_response response) {
        // 返回内容不是json，失败
        if (response.headers().content_type() != kContentTypeJson)
            throw http_exception(U("设置失败"));

        return response.extract_json();
    }).then([set5G](const value &ret) {
        // 按需检查返回值是否成功
        if (set5G)
        {
            if (ret.at(kResponseObject5G.data()).at(kReturnCode.data()).as_string() != kSuccessCode)
                throw http_exception(U("设置失败"));
        }
        else
        {
            if (ret.at(kResponseObject24G.data()).at(kReturnCode.data()).as_string() != kSuccessCode)
                throw http_exception(U("设置失败"));
        }
    });
}

int main(int argc, const char *argv[])
{
    if (argc == 3)
    {
        try
        {
            // 5G？
            const auto iArg5G = stoi(argv[1]);
            // 开？
            const auto iArgOn = stoi(argv[2]);
            switch (iArg5G)
            {
            case 0:
                switch (iArgOn)
                {
                case 0:
                    setting(false, false).wait();
                    break;
                case 1:
                    setting(false, true).wait();
                    break;
                default:
                    break;
                }
                break;
            case 1:
                switch (iArgOn)
                {
                case 0:
                    setting(true, false).wait();
                    break;
                case 1:
                    setting(true, true).wait();
                    break;
                default:
                    break;
                }
                break;
            default:
                break;
            }
        }
        catch (const http_exception &e)
        {
            ucout << e.what() << endl;
        }
        catch (const std::exception &e)
        {
            cout << e.what() << endl;
        }
    }
}
